﻿#include "Skybox.h"

#include "Asset/AssetManager.h"

Skybox::Skybox()
{
  m_cubeMap = AssetManager::LoadAsset<CubeMap>(
    "assets/textures/skybox/sun.png;"
    "assets/textures/skybox/moon.png;"
      "assets/textures/skybox/empty.png;"
      "assets/textures/skybox/empty.png;"
      "assets/textures/skybox/empty.png;"
      "assets/textures/skybox/empty.png"
    );
  m_shader = AssetManager::LoadAsset<Shader>("assets/shaders/src/Skybox");
}

void Skybox::SetTime(float time)
{
  m_time = time;
}

glm::vec4 Skybox::GetSkyColorByTime()
{
  const float time = GetTime() / GetDayLength();
  glm::vec4 skyColor;
  if (time < 0.1f)
    skyColor = glm::mix(glm::vec4(0.94f, 0.71f, 0.52f, 1.0f), glm::vec4(0.42f, 0.81f, 0.92f, 1.0f), (time) / 0.1);
  else if (time < 0.25f)
    skyColor = glm::mix(glm::vec4(0.42f, 0.81f, 0.92f, 1.0f), glm::vec4(0.49f, 0.89f, 0.92f, 1.0f), (time - 0.1) / 0.15);
  else if (time < 0.4f)
    skyColor = glm::mix(glm::vec4(0.49f, 0.89f, 0.92f, 1.0f), glm::vec4(0.42f, 0.81f, 0.92f, 1.0f), (time - 0.25) / 0.15);
  else if (time < 0.5f)
    skyColor = glm::mix(glm::vec4(0.42f, 0.81f, 0.92f, 1.0f), glm::vec4(0.94f, 0.71f, 0.52f, 1.0f), (time - 0.4) / 0.1);
  else if (time < 0.6f)
    skyColor = glm::mix(glm::vec4(0.94f, 0.71f, 0.52f, 1.0f), glm::vec4(0.15f, 0.23f, 0.25f, 1.0f), (time - 0.5) / 0.1);
  else if (time < 0.75f)
    skyColor = glm::mix(glm::vec4(0.15f, 0.23f, 0.25f, 1.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f), (time - 0.6) / 0.15);
  else if (time < 0.9f)
    skyColor = glm::mix(glm::vec4(0.0f, 0.0f, 0.0f, 1.0f), glm::vec4(0.05f, 0.03f, 0.0f, 1.0f), (time - 0.75) / 0.15);
  else
    skyColor = glm::mix(glm::vec4(0.05f, 0.03f, 0.0f, 1.0f), glm::vec4(0.94f, 0.71f, 0.52f, 1.0f), (time - 0.9) / 0.1);
  return skyColor;
}

glm::vec4 Skybox::GetSunshineColorByTime()
{
  const float time = GetTimeOfDay();
  glm::vec4 skyColor;
  if (time < 0.1f)
    skyColor = glm::mix(glm::vec4(0.94f, 0.71f, 0.52f, 1.0f), glm::vec4(0.77f, 0.75f, 0.70f, 1.0f), (time) / 0.1);
  else if (time < 0.25f)
    skyColor = glm::mix(glm::vec4(0.77f, 0.75f, 0.70f, 1.0f), glm::vec4(0.89f, 0.79f, 0.75f, 1.0f), (time - 0.1) / 0.15);
  else if (time < 0.4f)
    skyColor = glm::mix(glm::vec4(0.89f, 0.79f, 0.75f, 1.0f), glm::vec4(0.77f, 0.75f, 0.70f, 1.0f), (time - 0.25) / 0.15);
  else if (time < 0.5f)
    skyColor = glm::mix(glm::vec4(0.77f, 0.75f, 0.70f, 1.0f), glm::vec4(0.94f, 0.71f, 0.52f, 1.0f), (time - 0.4) / 0.1);
  else if (time < 0.6f)
    skyColor = glm::mix(glm::vec4(0.94f, 0.71f, 0.52f, 1.0f), glm::vec4(0.15f, 0.23f, 0.25f, 1.0f), (time - 0.5) / 0.1);
  else if (time < 0.75f)
    skyColor = glm::mix(glm::vec4(0.15f, 0.23f, 0.25f, 1.0f), glm::vec4(0.15f, 0.23f, 0.25f, 1.0f), (time - 0.6) / 0.15);
  else if (time < 0.9f)
    skyColor = glm::mix(glm::vec4(0.15f, 0.23f, 0.25f, 1.0f), glm::vec4(0.05f, 0.03f, 0.0f, 1.0f), (time - 0.75) / 0.15);
  else
    skyColor = glm::mix(glm::vec4(0.05f, 0.03f, 0.0f, 1.0f), glm::vec4(0.94f, 0.71f, 0.52f, 1.0f), (time - 0.9) / 0.1);

  return skyColor;
}

glm::vec3 Skybox::GetSunshineDirection()
{
  // 计算太阳光方向
  //if (timeOfDay >= 0.f && timeOfDay <= 0.5f)
    return -glm::rotate(GetTimeOfDay() * glm::two_pi<float>(), glm::vec3(0, 0, 1)) * glm::vec4(1, 0, 0, 0);
  //return glm::vec4(0, 1, 0, 0);
}

void Skybox::Update(const glm::mat4 &projection, const glm::mat4 &view, float deltaTime)
{
  static constexpr float _2pi = 2 * glm::pi<float>();
  m_time += m_timeSpeed * deltaTime;
  if (m_time > m_dayLength)
    m_time -= m_dayLength;
  // glm::mat3(view): 提取相机视图矩阵的旋转/缩放部分（去除平移）
  m_transform = projection * glm::mat4(glm::mat3(view));
}

void Skybox::Render()
{
  glDepthFunc(GL_LEQUAL);  // 将深度测试函数设置为GL_LEQUAL，这样天空盒就会被正确渲染在所有其他物体前面
  glDisable(GL_CULL_FACE); // 禁用面剔除，这样天空盒的背面也会被正确渲染

  m_shader->Bind();
  m_shader->SetTexture("uCubeMap", m_cubeMap, 1);
  m_shader->SetMat4("uTransform", m_transform * glm::rotate(m_time / m_dayLength * glm::two_pi<float>(), glm::vec3(0, 0, 1)));
  m_shader->SetVec4("uSkyColor", GetSkyColorByTime());

  m_vertexArray.DrawIndexed();

  glDepthFunc(GL_LESS);
  glEnable(GL_CULL_FACE);
}